package com.falcon.service.impl;

import com.falcon.config.GeneratorConfig;
import com.falcon.enums.JavaMappingTypeEnum;
import com.falcon.enums.MysqlColumnTypeEnum;
import com.falcon.mapper.IColumnMapper;
import com.falcon.mapper.IMysqlColumnMapper;
import com.falcon.mapper.IMysqlTableMapper;
import com.falcon.mapper.ITableMapper;
import com.falcon.service.IMysqlTableService;
import com.falcon.domain.req.MysqlTableQuery;
import com.falcon.domain.po.ColumnPO;
import com.falcon.domain.po.TablePO;
import com.falcon.domain.po.MysqlColumnPO;
import com.falcon.domain.po.MysqlTablePO;
import com.falcon.utils.PathUtil;
import com.swan.core.constants.NumberConstant;
import com.swan.core.utils.ListUtil;
import com.swan.core.utils.NameUtil;
import com.swan.mybatis.condition.Condition;
import com.swan.mybatis.condition.OpType;
import com.swan.mybatis.condition.SelectOption;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zongf
 * @since 2023-05-25
 **/
@Slf4j
@Service
public class MysqlTableService implements IMysqlTableService {

    @Autowired
    private IMysqlTableMapper mysqlTableMapper;

    @Autowired
    private IMysqlColumnMapper mysqlColumnMapper;

    @Autowired
    private ITableMapper genTableMapper;

    @Autowired
    private IColumnMapper genColumnMapper;

    @Autowired
    private GeneratorConfig generatorConfig;

    @Autowired
    private DataSource dataSource;

    private String schemaName;

    private String getSchemaName() {
        if (schemaName != null) {
            return this.schemaName;
        }
        try {
            Connection connection = dataSource.getConnection();

            schemaName = connection.getSchema();

            if (!StringUtils.hasText(schemaName)) {
                schemaName = connection.getCatalog();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return schemaName;
    }

    @Override
    public List<MysqlTablePO> queryNotImportTables(MysqlTableQuery query) {

        String schemaName = this.getSchemaName();

        // 1.查询 mysql 数据库中所有的表
        query.setTableSchema(schemaName);
        List<MysqlTablePO> tableList = this.mysqlTableMapper.selectList(query.toCondition());

        // 2.查询已导入的表
        List<TablePO> genTableList = this.genTableMapper.selectList(null);
        Map<String, TablePO> genTableMap = ListUtil.toMap(genTableList, TablePO::getTableName);

        // 过滤已导入的表
        List<MysqlTablePO> newTables = tableList.stream()
                .filter(existTable -> !genTableMap.containsKey(existTable.getTableName()))
                .sorted(Comparator.comparing(MysqlTablePO::getTableName))
                .collect(Collectors.toList());

        return newTables;
    }


    @Override
    public int importTables(List<String> tableNames) {

        // 1.查询数据库中所有的表
        Condition condition = Condition.newInstance()
                .and(MysqlTablePO.Fields.tableSchema, OpType.equals, schemaName);
        List<MysqlTablePO> tableList = this.mysqlTableMapper.selectList(condition);
        Map<String, MysqlTablePO> tableMap = ListUtil.toMap(tableList, MysqlTablePO::getTableName);

        // 2.查询数据库表中
        List<TablePO> genTableList = this.genTableMapper.selectList(null);
        Map<String, TablePO> genTableMap = ListUtil.toMap(genTableList, TablePO::getTableName);

        // 过滤需要执行导入操作的表
        List<MysqlTablePO> importTables = tableNames.stream()
                .filter(tableName -> !genTableMap.containsKey(tableName))
                .filter(tableName -> tableMap.containsKey(tableName))
                .map(tableName -> tableMap.get(tableName))
                .collect(Collectors.toList());

        // 入库&同步表结构
        for (MysqlTablePO mysqlTablePO : importTables) {
            TablePO tablePO = this.covert(mysqlTablePO);
            this.genTableMapper.insertNotNull(tablePO);

            this.sysTable(tablePO.getId());
        }

        return importTables.size();
    }


    @Override
    public void sysTable(Long tableId) {
        // 同步表
        TablePO tablePO = this.genTableMapper.selectById(tableId);


        Condition mysqlCondition = Condition.newInstance()
                .and(MysqlTablePO.Fields.tableSchema, OpType.equals, tablePO.getSchemaName())
                .and(MysqlTablePO.Fields.tableName, OpType.equals, tablePO.getTableName());
        MysqlTablePO mysqlTablePO = this.mysqlTableMapper.select(mysqlCondition);
        tablePO.setComment(mysqlTablePO.getTableComment());
        this.genTableMapper.updateNotNull(tablePO);

        // 查询表列
        Condition condition = Condition.newInstance()
                .and(MysqlColumnPO.Fields.tableSchema, OpType.equals, tablePO.getSchemaName())
                .and(MysqlColumnPO.Fields.tableName, OpType.equals, tablePO.getTableName());
        SelectOption option = SelectOption.newInstance().asc(MysqlColumnPO.Fields.ordinalPosition);
        List<MysqlColumnPO> mysqlColumnPOS = this.mysqlColumnMapper.selectList(condition, option);

        Map<String, MysqlColumnPO> mysqlColumnMap = ListUtil.toMap(mysqlColumnPOS, MysqlColumnPO::getColumnName);

        condition.clear()
                .and(ColumnPO.Fields.tableId, OpType.equals, tablePO.getId());
        List<ColumnPO> ColumnPOS = this.genColumnMapper.selectList(condition);
        Map<String, ColumnPO> genColumnMap = ListUtil.toMap(ColumnPOS, ColumnPO::getColumnName);

        // 筛选新字段
        List<ColumnPO> newColumnsList = mysqlColumnPOS.stream()
                .filter(genColumn -> !genColumnMap.containsKey(genColumn.getColumnName()))
                .map(mysqlColumnPO -> this.convertColumn(tableId, mysqlColumnPO))
                .collect(Collectors.toList());
        newColumnsList.forEach(this.genColumnMapper::insertNotNull);
        log.info("新增字段数:{}", newColumnsList.size());

        // 筛选已删除字段
        List<Long> deleteIds = ColumnPOS.stream()
                .filter(genColumn -> !mysqlColumnMap.containsKey(genColumn.getColumnName()))
                .map(ColumnPO::getId)
                .collect(Collectors.toList());
        if (!deleteIds.isEmpty()) {
            this.genColumnMapper.deleteByIds(deleteIds);
            log.info("删除字段数:{}", deleteIds.size());
        }

        // 筛选更新的字段
        List<ColumnPO> updateColumns = ColumnPOS.stream()
                .filter(genColumn -> mysqlColumnMap.containsKey(genColumn.getColumnName())).collect(Collectors.toList());
        updateColumns.forEach(genColumn -> this.mergeColumn(genColumn,mysqlColumnMap.get(genColumn.getColumnName())));
        ColumnPOS.forEach(this.genColumnMapper::updateNotNull);
        log.info("更新字段数:{}", updateColumns.size());
    }

    private void mergeColumn(ColumnPO ColumnPO, MysqlColumnPO mysqlColumnPO) {
        ColumnPO.setSchemaName(mysqlColumnPO.getTableSchema());
        ColumnPO.setTableName(mysqlColumnPO.getTableName());
        ColumnPO.setColumnName(mysqlColumnPO.getColumnName());
        ColumnPO.setColumnType(mysqlColumnPO.getDataType());
        ColumnPO.setColumnFullType(mysqlColumnPO.getColumnType());
        ColumnPO.setColumnDefault(mysqlColumnPO.getColumnDefault());
        ColumnPO.setColumnComment(mysqlColumnPO.getColumnComment());
        ColumnPO.setOrdinalPosition(mysqlColumnPO.getOrdinalPosition());
        ColumnPO.setCharMaxLength(mysqlColumnPO.getCharacterMaximumLength());
        ColumnPO.setNumMaxLength(mysqlColumnPO.getNumericPrecision());
        ColumnPO.setNumScale(mysqlColumnPO.getNumericScale());
        ColumnPO.setNullable(mysqlColumnPO.isNullAble());
        ColumnPO.setIsPk(mysqlColumnPO.isPK());
        ColumnPO.setAutoIncrement(mysqlColumnPO.isAutoIncrement());
        ColumnPO.setAutoTimeOnCreate(mysqlColumnPO.isAutoTimeOnCreate());
        ColumnPO.setAutoTimeOnUpdate(mysqlColumnPO.isAutoTimeOnUpdate());
        ColumnPO.setIsUnsigned(mysqlColumnPO.isUnsigned());
        ColumnPO.setStatus(NumberConstant.ONE);
        ColumnPO.setFieldType(JavaMappingTypeEnum.getType(MysqlColumnTypeEnum.of(ColumnPO.getColumnType()), ColumnPO.getIsUnsigned(), ColumnPO.getNumMaxLength()).toString());
        ColumnPO.setFieldName(NameUtil.toHumpName(ColumnPO.getColumnName()));
    }

    private ColumnPO convertColumn(Long tableId, MysqlColumnPO mysqlColumnPO) {
        ColumnPO ColumnPO = new ColumnPO();
        this.mergeColumn(ColumnPO, mysqlColumnPO);
        ColumnPO.setStatus(NumberConstant.ZERO);
        ColumnPO.setTableId(tableId);
        return ColumnPO;
    }


    public TablePO covert(MysqlTablePO table){
        TablePO tablePO = new TablePO();
        tablePO.setSchemaName(table.getTableSchema());
        tablePO.setTableName(table.getTableName());
        tablePO.setComment(table.getTableComment());
        tablePO.setClassName(PathUtil.handleClassName(table.getTableName(), generatorConfig.getTablePrefix()));
        return tablePO;
    }


}
